package cn.stylefeng.guns.modular.business.task;

/**
 * Description:
 *
 * @author lxc
 * @date 2019/1/20.10:49
 */


import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.DelayQueue;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

/**
 *任务调度系统
 *后台守护线程不断的执行检测工作
 * @author wangqiqi
 * @Date 2018年05月27日
 */
public class TaskQueueDaemonThread {

    //日志打印
    //private static final Logger LOG = Logger.getLogger(TaskQueueDaemonThread.class);

    private TaskQueueDaemonThread() {
    }

    private static class LazyHolder {
        private static TaskQueueDaemonThread taskQueueDaemonThread = new TaskQueueDaemonThread();
    }

    public static TaskQueueDaemonThread getInstance() {
        return LazyHolder.taskQueueDaemonThread;
    }

    Executor executor = Executors.newFixedThreadPool(20);

    /**
     * 缓存任务
     */
    private TaskDelay.ApplicationTask jedis = new TaskDelay.ApplicationTask(){
        public void execute() {
            JedisUtils.init(false);
        }
    };
    /**
     * 缓存任务1StudentTask
     */
    private TaskDelay.ApplicationTask surveyTask = new TaskDelay.ApplicationTask(){
        public void execute() {
            //初始化任务对象
            Student s=new Student();
            //设置任务名称，从缓存中去相应的任务
            s.setTaskName("sms");
            getDelayMap(s);
        }
    };

    /**
     * 管理任务
     */
    public void manage(){
        TaskDelay.getInstance()
                .addTask(jedis)
                .addTask(surveyTask);
    }
    /**
     * 缓存任务2
     */
    /**
     * 守护线程
     */
    private Thread daemonThread;

    /**
     * 初始化守护线程以及任务
     */
    public void init() {
        daemonThread = new Thread(() -> execute());
        /*daemonThread.setDaemon(true);
        daemonThread.setName("Task Queue Daemon Thread");*/
        daemonThread.start();
        manage();
        TaskDelay.getInstance().startTask();
    }

    private void execute() {
        System.out.println("start:" + System.currentTimeMillis());
        while (true) {
            try {
                //从延迟队列中取值,如果没有对象过期则队列一直等待，
                Task<?> t1 = t.take();
                if (t1 != null) {
                    //修改问题的状态
                    Object task = t1.getTask();
                    System.out.println(task);

                    if (task == null) {
                        continue;
                    }
                    delDelayTask(task);
                    //executor.execute(task);//不涉及线程池不适用
                    //LOG.info("[at task:" + task + "]   [Time:" + System.currentTimeMillis() + "]");
                }
            } catch (Exception e) {
                e.printStackTrace();
                break;
            }
        }
    }

    /**
     * 创建一个最初为空的新 DelayQueue,内存的可见性
     */
    private volatile DelayQueue<Task<?>> t = new DelayQueue<>();

    /**
     * Object规定属性1、delayTime（延迟时间）2、taskName（多个延迟任务名字必须唯一）3、自增唯一id
     * 添加任务，
     * time 延迟时间,ms
     * task 任务
     * 用户为问题设置延迟时间
     */
    public void put(long time, Object task) {
        //转换成ns
        long nanoTime = TimeUnit.NANOSECONDS.convert(time, TimeUnit.MILLISECONDS);
        //创建一个任务
        Task<?> k = new Task<>(nanoTime, task);
        //将任务放在延迟的队列中
        t.put(k);

        addDelayTask(task);
    }
    /**
     * 获取延迟任务Map
     * @param task
     * @param id
     */
    public void getDelayMap(Object task){
        if(JedisUtils.exists(getDelayTaskName(task))){
            Map<String,Object> map=JedisUtils.getObjectMap(getDelayTaskName(task));
            long delayTime;
            long addTimeMillis;
            long nowTimeMillis;
            for(String s:map.keySet()){
                delayTime = Long.parseLong(getDelayTime(map.get(s)));
                addTimeMillis = Long.parseLong(getDelayAddTimeMillis(map.get(s)));
                nowTimeMillis = System.currentTimeMillis();
                delayTime=delayTime+addTimeMillis-nowTimeMillis;
                if(delayTime<0){
                    delayTime=0;
                }
                TaskQueueDaemonThread.getInstance().put(delayTime,map.get(s));
            }
        }
    }
    /**
     * 延迟任务add
     * @param task
     * @param id
     */
    public void addDelayTask(Object task) {
        Map<String,Object>  map=new HashMap<>();
        map.put(getDelayId(task), task);
        if(JedisUtils.exists(getDelayTaskName(task))){
            JedisUtils.mapObjectPut(getDelayTaskName(task), map);
        }else{
            JedisUtils.setObjectMap(getDelayTaskName(task),map,0);
        }
    }
    /**
     * 延迟任务del
     * @param task
     * @param id
     */
    public long delDelayTask(Object task) {
        long result = 0;
        if(JedisUtils.mapExists(getDelayTaskName(task),getDelayId(task))){
            result = JedisUtils.mapRemove(getDelayTaskName(task),getDelayId(task));
        }
        return result;
    }

    /**
     * @param task
     */
    public boolean endTask(Task<Runnable> task){
        return t.remove(task);
    }
    /**
     * 延迟id
     * @param task
     * @return
     */
    public String getDelayId(Object task) {
        return getFieldValueByName("id", task).toString();
    }
    /**
     * 延迟任务名称
     * @param task
     * @return
     */
    public String getDelayTaskName(Object task) {
        return getFieldValueByName("taskName", task).toString();
    }
    /**
     * 延迟时间
     * @param task
     * @return
     */
    public String getDelayTime(Object task) {
        return getFieldValueByName("delayTime", task).toString();
    }
    /**
     * 延迟时间
     * @param task
     * @return
     */
    public String getDelayAddTimeMillis(Object task) {
        return getFieldValueByName("addTimeMillis", task).toString();
    }
    /**
     * 根据属性名获取属性值
     * */
    public Object getFieldValueByName(String fieldName, Object o) {
        try {
            String firstLetter = fieldName.substring(0, 1).toUpperCase();
            String getter = "get" + firstLetter + fieldName.substring(1);
            Method method = o.getClass().getMethod(getter, new Class[] {});
            Object value = method.invoke(o, new Object[] {});
            return value;
        } catch (Exception e) {
            return null;
        }
    }
}
